//
// Created by Administrator on 2024/6/24.
//

#ifndef MESSAGEPACKREADER_H
#define MESSAGEPACKREADER_H
#include <cstdint>
#include <string>
#include <boost/endian/conversion.hpp>
#include <boost/crc.hpp>

#include "../logger.hpp"
#include "../error.hpp"
#include "../concepts.hpp"

namespace common {
    inline void range_check(size_t capacity, size_t pos, size_t read) {
        if (pos + read > capacity)
        {
            ERR(common::recorder,"capacity:{}, position:{}, try to read {} more byte(s)", capacity, pos, read);
            ENABLE_STACKTRACE
            throw common::Error(common::error::NoEnoughBytesToRead, "No byte to read");
        }
    }

    class MessagePackReader {
        logger_t logger = common::Logger::logger("MessagePackReader");
        // 解码后的报文
        std::shared_ptr<char[]> reader_;
        // 源报文
        std::shared_ptr<char[]> src_buffer_;
        // 源报文长度
        size_t length_;
        // 解码后的报文长度
        size_t reader_length_;
        size_t position_;
        // 是否已解码+校验
        bool is_decoded_ = false;

        std::function<bool(void*, uint8_t&)> escaper_ ;

        // 转义字节
        static constexpr uint16_t decode7d02 = 0x7d02;
        static constexpr uint16_t decode7d01 = 0x7d01;

        static bool tryDecode(char *buffer, uint8_t& decoded) {
            const auto buffer_ = reinterpret_cast<uint8_t*>(buffer);
            const uint16_t bytes = (*buffer_) << 8 | *(buffer_+1);
            if (bytes == decode7d02) {
                decoded = 0x7e;
                return true;
            }
            if (bytes == decode7d01) {
                decoded = 0x7d;
                return true;
            }
            decoded = bytes >> 8 & 0xff;
            return false;
        }

    public:
        MessagePackReader(const char *buffer, const size_t length,
            const std::function<bool(void*, uint8_t&)>& escaper = [] (void*, uint8_t&) { return false; })
            : length_(length), reader_length_(0), position_(0), escaper_(escaper) {
            src_buffer_ = std::make_shared<char[]>(length);
            reader_ = std::make_shared<char[]>(length);
            memcpy(src_buffer_.get(), buffer, length);
        }

        ~MessagePackReader() {
        }

        void decode() {
            if (is_decoded_) return;

            uint8_t byte;
            for (int i = 0; i < length_; ++i) {
                if (escaper_(src_buffer_.get()+i, byte)) {
                    i++;
                }
                memcpy(reader_.get()+reader_length_++, &byte, 1);
            }
            memcpy(reader_.get()+reader_length_++, src_buffer_.get()+length_-1, 1);

            is_decoded_ = true;
        }

        bool validate(const std::function<bool(const char*, const char*)> &checksum_validator) {
            if (!is_decoded_) decode();
            return checksum_validator(reader_.get()+1, reader_.get()+reader_length_-2);
            // boost::crc_ccitt_false_t computer;
            // computer.process_bytes(reader_+1, reader_length_-1-3);
            // const auto crc = computer.checksum();
            // const auto net = boost::endian::native_to_big(crc);
            // return net == *reinterpret_cast<uint16_t*>(reader_+reader_length_-3);
        }

        void readByte(uint8_t &value) {
            decode();
            range_check(reader_length_, position_, sizeof(uint8_t));
            value = *(reinterpret_cast<std::uint8_t *>(reader_.get()) + position_++);
        }

        template<size_t N>
        void readBytes(uint8_t (&bytes)[N]);

        template <class T>
        void readUint(T &var) {
            decode();
            range_check(reader_length_, position_, sizeof(T));
            auto read = *reinterpret_cast<T*>(reader_.get() + position_);
            var = boost::endian::big_to_native(read);
            position_ += sizeof(T);
        }

        template<size_t N>
        void readString(char (&)[N]);

        std::string readString() {
            decode();
            int str_end = position_;
            while (str_end < reader_length_ && reader_[str_end] != 0)
                str_end++;
            if (str_end == reader_length_) return "";
            auto result = std::string(reader_.get()+position_, reader_.get()+str_end);
            position_ = str_end+1;
            return result;
        }

        void readTime(time_t &time) {
            decode();
            range_check(reader_length_, position_, sizeof(time_t));
            time = boost::endian::big_to_native(*reinterpret_cast<time_t*>(reader_.get()+position_));
            position_ += sizeof(time_t);
        }

        template<typename T>
        void readPointer(T *data, size_t size) {
            ERR(logger, "type not support!");
            throw common::Error(common::error::WrongFlag, "type not support");
        }

        template<>
        void readPointer<uint8_t>(uint8_t *data, size_t size);

        template<>
        void readPointer<char>(char *data, size_t size);

        template<common::BasicType T>
        void readValueAt(T &v, const size_t at, const bool to_native = true) {
            decode();
            v = *reinterpret_cast<T*>(reader_.get() + at);
            if (to_native)
                v = boost::endian::big_to_native(v);
        }

        template<common::BasicType T>
        void readValueBefore(T &v, const size_t before, const bool to_native = true) {
            decode();
            v = *reinterpret_cast<T*>(reader_.get() + position_ - before);
            if (to_native)
                v = boost::endian::big_to_native(v);
        }
    };

    template<>
    inline void MessagePackReader::readPointer<unsigned char>(uint8_t *data, size_t size) {
        decode();
        range_check(reader_length_, position_, size);
        memcpy(data, reader_.get()+position_, size);
        position_ += size;
    }

    template<>
    inline void MessagePackReader::readPointer<char>(char *data, size_t size) {
        decode();
        range_check(reader_length_, position_, size);
        std::copy_n(reader_.get()+position_, size, data);
        position_ += size;
    }

    template<size_t N>
    void MessagePackReader::readBytes(uint8_t(&bytes)[N]) {
        decode();
        range_check(reader_length_, position_, N);
        memcpy(bytes, reader_.get()+position_, N);
        position_ += N;
    }

    template<size_t N>
    void MessagePackReader::readString(char(&str)[N]) {
        decode();
        range_check(reader_length_, position_, N);
        std::copy_n(reader_.get()+position_, N, str);
        position_ += N;
    }
}



#endif //MESSAGEPACKREADER_H